package com.seven.ssa.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.seven.ssa.common.ErrorCode;
import com.seven.ssa.exception.BusinessException;
import com.seven.ssa.model.dto.questionBankQuestion.QuestionBankQuestionBatchAddRequest;
import com.seven.ssa.model.dto.questionBankQuestion.QuestionBankQuestionBatchRemoveRequest;
import com.seven.ssa.model.entity.QuestionBankQuestion;
import com.seven.ssa.service.QuestionBankQuestionService;
import com.seven.ssa.mapper.QuestionBankQuestionMapper;
import com.seven.ssa.service.QuestionBankService;
import com.seven.ssa.service.QuestionService;
import com.seven.ssa.service.UserService;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.context.annotation.Lazy;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
* @author Dell
* @description 针对表【question_bank_question(题库题目)】的数据库操作Service实现
* @createDate 2025-06-14 10:41:48
*/
@Service
public class QuestionBankQuestionServiceImpl extends ServiceImpl<QuestionBankQuestionMapper, QuestionBankQuestion>
    implements QuestionBankQuestionService{

    @Resource
    @Lazy
    private QuestionService questionService;
    @Resource
    private QuestionBankService questionBankService;
    @Resource
    private UserService userService;

    /**
     * 校验库目关系当中的参数是否为空
     * @param questionBankQuestion
     */
    @Override
    public void validQuestionBankQuestion(QuestionBankQuestion questionBankQuestion) {
        // 判断库目关系是否为空
        if(questionBankQuestion == null){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        // 判断库目关系当中的题库id，题目id，用户id是否为空
        if(ObjectUtils.anyNull(questionBankQuestion.getQuestionBankId(),
                               questionBankQuestion.getQuestionId(),
                               questionBankQuestion.getUserId())){
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
        }
        // 判断库目关系当中的题库id，题目id对应的题库，题目是否存在
        if(ObjectUtils.anyNull(questionService.getById(questionBankQuestion.getQuestionId()),
                               questionBankService.getById(questionBankQuestion.getQuestionBankId()))){
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
        }
    }

    /**
     * P3：批量往题库中加题目
     * @param questionBankQuestionBatchAddRequest
     * @param request
     */
    @Override
    public void batchAddQuestionsToBank(QuestionBankQuestionBatchAddRequest questionBankQuestionBatchAddRequest, HttpServletRequest request) {
        // 判断请求是否为空
        if (questionBankQuestionBatchAddRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        // 判断请求当中的题库id，题目id列表是否为空
        Long questionBankId = questionBankQuestionBatchAddRequest.getQuestionBankId();
        List<Long> questionIdList = questionBankQuestionBatchAddRequest.getQuestionIdList();
        if (ObjectUtils.anyNull(questionBankId, questionIdList)) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
        }
        // 判断请求当中的题库id对应的题库是否存在
        if (ObjectUtils.anyNull(questionBankService.getById(questionBankId))) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
        }
        // P3：健壮性：检查哪些题目存在于题库中，以避免重复插入
        LambdaQueryWrapper<QuestionBankQuestion> lambdaQueryWrapper = Wrappers.lambdaQuery(QuestionBankQuestion.class)
                .eq(QuestionBankQuestion::getQuestionBankId, questionBankId)
                .in(QuestionBankQuestion::getQuestionId, questionIdList);
        List<QuestionBankQuestion> existQuestionList = this.list(lambdaQueryWrapper);
        // 存在的题目的Id集合
        Set<Long> existQuestionIdSet = existQuestionList.stream()
                .map(QuestionBankQuestion::getQuestionId)
                .collect(Collectors.toSet());
        // 过滤掉，存在的题目的Id（集合中）
        questionIdList = questionIdList.stream().filter(questionId -> {
            return !existQuestionIdSet.contains(questionId);
        }).collect(Collectors.toList());
        if (CollUtil.isEmpty(questionIdList)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "您所选择的所有题目，都已经存在于指定题库中！");
        }
        // P3：健壮性：结束

        // P3：性能改良：并发编程（自定义线程池）
        ThreadPoolExecutor customExecutor = new ThreadPoolExecutor(
                20,                         // 核心线程数
                50,                        // 最大线程数
                60L,                       // 线程空闲存活时间
                TimeUnit.SECONDS,           // 存活时间单位
                new LinkedBlockingQueue<>(10000),  // 阻塞队列容量
                new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略：由调用线程处理任务
        );
        // 用于保存所有批次的 CompletableFuture
        List<CompletableFuture<Void>> futures = new ArrayList<>();
        // P3：性能改良：结束

        // P3：稳定性：分批处理，请求当中的题目id（列表中）
        int batchSize = 1000;
        int questionIdListSize = questionIdList.size();
        for (int i = 0; i < questionIdListSize; i++) {
            List<Long> subQuestionIdList = questionIdList.subList(i, Math.min(i+batchSize,questionIdListSize));
            // for(Long questionId : subQuestionIdList)
            List<QuestionBankQuestion> questionBankQuestionList = subQuestionIdList.stream().map(questionId -> {
                QuestionBankQuestion questionBankQuestion = new QuestionBankQuestion();
                questionBankQuestion.setQuestionBankId(questionBankId);
                questionBankQuestion.setQuestionId(questionId);
                questionBankQuestion.setUserId(userService.getLoginUser(request).getId());
                return questionBankQuestion;
            }).collect(Collectors.toList());
            // 获取当前实现类的代理对象，来调用事务方法。因为，Spring事务依赖于代理机制，如果用this调用就不会通过Spring代理，就不会触发事务。
            // 必须在启动类加入注解，开启切面自动代理：@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
            QuestionBankQuestionService questionBankQuestionService = (QuestionBankQuestionServiceImpl) AopContext.currentProxy();
            // P3：性能改良：并发编程（自定义线程池）：异步处理每批数据并添加到 futures 列表
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                questionBankQuestionService.batchAddQuestionsToBankInner(questionBankQuestionList);
            }, customExecutor).exceptionally(ex->{
                log.error("批处理，失败！",ex);
                return null;
            });
            futures.add(future);
            // P3：性能改良：并发编程（自定义线程池）：等待所有批次操作完成后，关闭线程池
            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
            customExecutor.shutdown();
            // P3：性能改良：结束
            questionBankQuestionService.batchAddQuestionsToBankInner(questionBankQuestionList);
        }
        // P3：稳定性：结束
    }
    /**
     * P3：批量往题库中加题目
     * @param questionBankQuestionBatchRemoveRequest
     * @param request
     */
    @Override
    public void batchRemoveQuestionsToBank(QuestionBankQuestionBatchRemoveRequest questionBankQuestionBatchRemoveRequest, HttpServletRequest request) {
        // 判断请求是否为空
        if(questionBankQuestionBatchRemoveRequest == null){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        // 判断请求当中的题库id，题目id列表是否为空
        if(ObjectUtils.anyNull(questionBankQuestionBatchRemoveRequest.getQuestionBankId(),
                questionBankQuestionBatchRemoveRequest.getQuestionIdList())){
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
        }
        // 判断请求当中的题库id对应的题库是否存在
        if(ObjectUtils.anyNull(questionBankService.getById(questionBankQuestionBatchRemoveRequest.getQuestionBankId()))){
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
        }
        // 判断请求当中的题目id列表当中的题目id对应的题目是否存在。若存在，复用QBQ_Controller中的“删”方法即可
        for(Long questionId : questionBankQuestionBatchRemoveRequest.getQuestionIdList()){
            if(ObjectUtils.anyNull(questionService.getById(questionId))){
                throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
            }
            // 构造查询
            LambdaQueryWrapper<QuestionBankQuestion> lambdaQueryWrapper = Wrappers.lambdaQuery(QuestionBankQuestion.class)
                    .eq(QuestionBankQuestion::getQuestionId, questionId)
                    .eq(QuestionBankQuestion::getQuestionBankId, questionBankQuestionBatchRemoveRequest.getQuestionBankId());
            // 进行删除操作
            boolean result = this.remove(lambdaQueryWrapper);
            if (!result) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR);
            }
        }
    }

    /**
     * P3：稳定性：分批处理，把批量从题库中增加题目的方法，提取出来
     * @param questionBankQuestionList
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void batchAddQuestionsToBankInner(List<QuestionBankQuestion> questionBankQuestionList) {
            // P3：健壮性：细化异常处理
            try{
                // P3：性能改良：从save改saveBatch
                boolean result = this.saveBatch(questionBankQuestionList);
                if (!result) {
                    throw new BusinessException(ErrorCode.OPERATION_ERROR,"批量从题库中增加题目，失败！");
                }
            }catch(DataIntegrityViolationException e){
                throw new BusinessException(ErrorCode.OPERATION_ERROR,"题目已经存在于指定题库中！");
            }
            catch(DataAccessException e){
                throw new BusinessException(ErrorCode.OPERATION_ERROR,"数据库出现连接或操作问题！");
            }
            catch(Exception e){
                throw new BusinessException(ErrorCode.OPERATION_ERROR,"批量从题库中增加题目，失败！");
            }
            // P3：健壮性：结束
    }
}




